package com.sugou.dao.support.impl;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.hql.spi.QueryTranslatorFactory;
import org.springframework.stereotype.Repository;

import com.sugou.dao.exceptions.DaoException;
import com.sugou.dao.support.Dao;
import com.sugou.dao.support.bean.ErrorResInfo;
import com.sugou.dao.support.bean.PagesList;
import com.sugou.dao.support.bean.PagesSearchStatement;
import com.sugou.dao.support.bean.ResultList;
import com.sugou.dao.support.bean.SearchStatement;
import com.sugou.model.core.IdEntity;

@Repository
public class BaseDao<E extends Serializable> implements Dao<E> {
	private static final long serialVersionUID = 3167179917089818835L;
	
	@Resource(name="sessionFactory")
	private SessionFactory sessionFactory;

	@Resource(name="queryTranslatorFactory")
	private QueryTranslatorFactory queryTranslatorFactory;
	
	public Session getSession(){
		return this.sessionFactory.getCurrentSession();
	}
	
	public Number insert(E entity) {
		this.getSession().save(entity);
		return 1;
	}

	
	public Number update(E entity) {
		if(entity instanceof IdEntity){
			if(((IdEntity) entity).getId()!=null)
				this.getSession().update(entity);
			else
				return 0;
			return 1;
		}else{
			return 0;
		}
	}

	
	public Number save(E entity) {
		this.getSession().saveOrUpdate(entity);
		return 1;
	}

	
	public List<E> list(CharSequence stat) {
		if(stat instanceof PagesSearchStatement){
			return this.pageList((PagesSearchStatement) stat);
		}else if(stat instanceof SearchStatement){
			ResultList<Serializable> reslist=new ResultList<Serializable>();
			Query query=this.getSession().createQuery(((SearchStatement) stat).getContent());
			this.setQueryParam(query, ((SearchStatement) stat).getParams());
			return new ResultList<E>(query.list());
		}else{
			ResultList<Serializable> reslist=new ResultList<Serializable>();
			Query query=this.getSession().createQuery(stat.toString());
			return new ResultList<E>(query.list());
		}
	}
	
	protected List<E> pageList(PagesSearchStatement stat){
		PagesList<E> resList=new PagesList<E>();
		List<? extends Serializable> params=stat.getParams();
		resList.setTotal(this.countRes(this.hqlToSql(stat.getContent()), params));
		resList.setSize(stat.getSize());
		resList.setPage(stat.getPage());
		int first=(stat.getPage()-1)*stat.getSize();
		Query query=this.getSession().createQuery(stat.getContent()).setFirstResult(first).setMaxResults(stat.getSize());
		this.setQueryParam(query, params);
		resList.setTags(stat.getTags());
		resList.addAll(query.list());
		return resList;
	}
	
	/**
	 * hql转换成sql
	 * @param hql hql语句
	 * @return 转换后的sql
	 */
	protected String hqlToSql(String hql){
		Map map= new HashMap();
		QueryTranslator trans=this.queryTranslatorFactory.createFilterTranslator(hql, hql, map, (SessionFactoryImplementor) this.getSessionFactory());
		trans.compile(map, false);
		
		return trans.getSQLString();
	}
	
	protected int countRes(String sql,List<? extends Object> params){
		StringBuilder countsql=new StringBuilder("select count(1) from (").append(sql).append(") _cntEntity_temp");
		Query query=this.getSession().createSQLQuery(countsql.toString());
		if(params!=null){
			this.setQueryParam(query, params);
		}
		return Integer.valueOf(query.list().get(0).toString());
	}
	
	protected void setQueryParam(Query query,List<? extends Object> params){
		for (int i = 0; i < params.size(); i++) {
			Object param=params.get(i);
			if(param instanceof Integer){
				query.setInteger(i, (Integer) param);
			}else if(param instanceof Enum){
				query.setInteger(i, ((Enum) param).ordinal());
			}else if(param instanceof Long){
				query.setLong(i, (Long) param);
			}else if(param instanceof Character){
				query.setCharacter(i, ((Character) param).charValue());
			}else if(param instanceof Date){
				query.setDate(i, (Date) param);
			}else if(param instanceof Double){
				query.setDouble(i, (Double) param);
			}else if(param instanceof Float){
				query.setFloat(i, (Float) param);
			}else if(param instanceof IdEntity){
				query.setEntity(i, param);
			}else if(param instanceof Number){
				query.setInteger(i, ((Number) param).intValue());
			}else if(param instanceof byte[]){
				query.setBinary(i, (byte[]) param);
			}else if(param instanceof CharSequence){
				query.setString(i, param.toString());
			}else if(param instanceof Serializable){
				query.setSerializable(i, (Serializable) param);
			}
		}
	}
	
	
	public QueryTranslatorFactory getQueryTranslatorFactory() {
		return queryTranslatorFactory;
	}

	public void setQueryTranslatorFactory(
			QueryTranslatorFactory queryTranslatorFactory) {
		this.queryTranslatorFactory = queryTranslatorFactory;
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Number remove(E entity) {
		this.getSession().delete(entity);
		return 1;
	}

	public IdEntity getEntity(Serializable tpl,Serializable... params) {
		if(tpl instanceof SearchStatement){
			ResultList<Serializable> reslist=new ResultList<Serializable>();
			Query query=this.getSession().createQuery(((SearchStatement) tpl).getContent());
			this.setQueryParam(query, ((SearchStatement) tpl).getParams());
			return (IdEntity)query.uniqueResult();
		}else if(tpl instanceof Class){
			if(params.length<1){
				throw new DaoException(new ErrorResInfo(-1, "请传入id值"));
			}
			Query query=this.getSession().createQuery("from "+((Class) tpl).getName()+" where id=?");
			this.setQueryParam(query, Arrays.asList(params));
			return (IdEntity) query.uniqueResult();
		}else if(tpl instanceof CharSequence){
			Query query=this.getSession().createQuery(tpl.toString());
			this.setQueryParam(query, Arrays.asList(params));
			return (IdEntity) query.uniqueResult();
		}else{
			throw new DaoException(new ErrorResInfo(-1, "无法解析查询"));
		}
		
	}
}
